/*
 * Copyright (c) 2009, Jan Pastor. All Rights Reserved.
 */

package sk.jp.jitag.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.border.Border;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import sk.jp.commons.exception.IllegalInputException;
import sk.jp.commons.ui.ExceptionDialog;
import sk.jp.commons.ui.FileChooser;
import sk.jp.commons.ui.InfoDialog;
import sk.jp.commons.ui.LeaveGridLayout;
import sk.jp.commons.ui.ListOption;
import sk.jp.commons.ui.ListOptionCellRenderer;
import sk.jp.commons.ui.TextDialog;
import sk.jp.commons.ui.WarningDialog;
import sk.jp.commons.utils.LogMsg;
import sk.jp.commons.utils.StringUtils;
import sk.jp.commons.utils.ExceptionUtils;
import sk.jp.jitag.model.config.Config;
import sk.jp.jitag.model.config.MediaType;
import sk.jp.jitag.model.config.Profile;
import sk.jp.jitag.model.config.Tool;
import sk.jp.jitag.model.config.FileFormat;
import sk.jp.jitag.ui.common.IconLoader;
import sk.jp.jitag.ui.common.Icons;
import sk.jp.jitag.util.Defaults;
import sk.jp.jitag.util.Msg;
import sk.jp.jitag.util.Property;
import sk.jp.jitag.dao.JitagDao;

/**
 * Preferences dialog.
 */
public class PreferencesDialog extends JDialog implements ActionListener {

    private static final Logger log = LoggerFactory.getLogger(PreferencesDialog.class);

    private enum ActionCode {
        save,
        cancel,
        selectConfigPathDirectory,
        addRepsitoryLocation,
        removeRepsitoryLocation,
        confPlacesSelected,
        lafSelected,
        lafThemeSelected,
        scanPeriodPresetSelected,
        openSelectMediaDialog,
        openFormatToolsDialog,
        ;
    }

    private Map<Property,String> tmpProperties;
    private Profile profile;
    private List<Tool> tools;

    private JitagDao jitagDao;

    private JTextField label = null;
    private JTextField description = null;
    private JList locations = null;
    private JTextField scanPeriod = null;
    private JComboBox periods = null;
    private JComboBox laf = null;
    private JComboBox lafTheme = null;
    private JComboBox confPlaces = null;
    private JTextField confPath = null;
    private JButton confPathSelectDir = null;
    private JComboBox language = null;
    private JButton btnCancel = null;

    //TODO Dokoncit!!! + readOnlyMode: boolean (all file modif operations are RO - safe mode)
    //TODO Dokoncit!!! + modifyJpegFiles: boolean (true = updatuje IPTC, rotacia)


    //----------------------------------------------------------------------

    /**
     * Initialize dialog.
     * @param ownerFrame
     * @param config current app configuration.
     * @param jitagDao app DAO.
     */
    public PreferencesDialog(JFrame ownerFrame, Config config, JitagDao jitagDao) {
        super(ownerFrame, Msg.prefDialogTitle.value(), true); //Preferences

        this.jitagDao = jitagDao;

        tmpProperties = new HashMap<Property, String>(3);
        tmpProperties.put(Property.confPlace, Property.confPlace.value());
        tmpProperties.put(Property.confPath, Property.confPath.value());
        tmpProperties.put(Property.uiLocale, Property.uiLocale.value());

        profile = config.getCurrentProfile();
        tools = config.getCurrentProfile().getTools();
    }

    //----------------------------------------------------------------------

    /**
     * Opens dialog with current configuration profile values.
     */
    public void open(){
        init(profile);
        pack();
        btnCancel.requestFocusInWindow();

        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screenDimension = toolkit.getScreenSize();
        Dimension size = getSize();
        setLocation((screenDimension.width - size.width) / 2,
                    (screenDimension.height - size.height) / 2);

        setVisible(true);
    }

    /**
     * Returns new properties or null if dialog eas closed by cancel.
     * <p>If called before {@link #open()}, returns original properties.
     * @return new properties values or null if dialog eas closed by cancel.
     */
    public Map<Property, String> getNewProperties() {
        log.trace(LogMsg.out("tmpProperties", tmpProperties));
        return tmpProperties;
    }

    /**
     * Returns new profile values or null if dialog eas closed by cancel.
     * <p>If called before {@link #open()}, returns original profile values.
     * @return new profile values or null if dialog eas closed by cancel.
     */
    public Profile getNewProfile() {
        log.trace(LogMsg.out("profile", profile));
        return profile;
    }

    //----------------------------------------------------------------------

    @Override
    public void setVisible(boolean visible) {
        if(!visible){
            //on close
            actionCancel();
        }
        super.setVisible(visible);
    }

    private void init(Profile profile){
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());

        // // //
        LeaveGridLayout leaveGridLayout;
        leaveGridLayout = new LeaveGridLayout(1, 2, 4, 5,
                                                LeaveGridLayout.HAlign.full,
                                                LeaveGridLayout.VAlign.center);
        JPanel mainPanel = new JPanel(leaveGridLayout);
        JScrollPane mainPannelScrollable = new JScrollPane(mainPanel);
        contentPane.add(mainPannelScrollable, BorderLayout.CENTER);
        mainPanel.setBorder(BorderFactory.createEmptyBorder(6, 4, 6, 4));

        // //
        leaveGridLayout = new LeaveGridLayout(4, 3, 4, 5,
                                                LeaveGridLayout.HAlign.left,
                                                LeaveGridLayout.VAlign.center);
        leaveGridLayout.setColumnsHAlign(LeaveGridLayout.HAlign.right,
                                         LeaveGridLayout.HAlign.center,
                                         LeaveGridLayout.HAlign.full,
                                         LeaveGridLayout.HAlign.left);
        JPanel mainPanel0 = new JPanel(leaveGridLayout);
        mainPanel.add(mainPanel0);
        Border lineBorder0 = BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1);
        mainPanel0.setBorder(BorderFactory.createTitledBorder(lineBorder0, Msg.prefDialogGeneral.value())); //General settings
        //
        mainPanel0.add(new JLabel(Msg.prefDialogConfPlace.value()));//Configuration and data place:
        mainPanel0.add(createHelpButton(Msg.prefDialogConfPlaceHelp)); //help for field

        ComboBoxModel listModel = ListOption.mapToListOptionModel(Defaults.confPlaces);
        confPlaces = new JComboBox(listModel);
        mainPanel0.add(confPlaces);
        confPlaces.setRenderer(new ListOptionCellRenderer());
        confPlaces.addActionListener(this);
        confPlaces.setActionCommand(ActionCode.confPlacesSelected.name());
        Defaults.ConfigPlace _configPlace = Defaults.ConfigPlace.valueOf(tmpProperties.get(Property.confPlace));
        confPlaces.setSelectedItem(ListOption.getOption(listModel, _configPlace));

        mainPanel0.add(new JLabel(" "));
        //
        mainPanel0.add(new JLabel(Msg.prefDialogConfPath.value()));//Configuration and data directory:
        mainPanel0.add(createHelpButton(Msg.prefDialogConfPathHelp)); //help for field
        confPath = new JTextField(tmpProperties.get(Property.confPath), 30);
        confPath.setEditable(false);
        mainPanel0.add(confPath);
        confPathSelectDir = createImgButton(Icons.folder, ActionCode.selectConfigPathDirectory);
        mainPanel0.add(confPathSelectDir);
        if(Defaults.ConfigPlace.valueOf(tmpProperties.get(Property.confPlace)) == Defaults.ConfigPlace.path){
            confPathSelectDir.setEnabled(true);
        }else{
            confPathSelectDir.setEnabled(false);
        }
        //
        mainPanel0.add(new JLabel(Msg.prefDialogLang.value()));//Language:
        mainPanel0.add(createHelpButton(Msg.prefDialogLangHelp)); //help for field
        listModel = ListOption.mapToListOptionModel(Defaults.locales);
        language = new JComboBox(listModel);
        mainPanel0.add(language);
        language.setRenderer(new ListOptionCellRenderer());
        language.setSelectedItem(ListOption.getOption(listModel, tmpProperties.get(Property.uiLocale)));
        mainPanel0.add(new JLabel(" "));

        // //
        leaveGridLayout = new LeaveGridLayout(4, 8, 4, 5,
                                                LeaveGridLayout.HAlign.left,
                                                LeaveGridLayout.VAlign.center);
        leaveGridLayout.setColumnsHAlign(LeaveGridLayout.HAlign.right,
                                         LeaveGridLayout.HAlign.center,
                                         LeaveGridLayout.HAlign.full,
                                         LeaveGridLayout.HAlign.left);
        leaveGridLayout.setRowsVAlign(LeaveGridLayout.VAlign.center,
                                      LeaveGridLayout.VAlign.center,
                                      LeaveGridLayout.VAlign.top,
                                      LeaveGridLayout.VAlign.center,
                                      LeaveGridLayout.VAlign.center,
                                      LeaveGridLayout.VAlign.center,
                                      LeaveGridLayout.VAlign.top,
                                      LeaveGridLayout.VAlign.top,
                                      LeaveGridLayout.VAlign.top);
        JPanel mainPanel1 = new JPanel(leaveGridLayout);
        mainPanel.add(mainPanel1);
        Border lineBorder1 = BorderFactory.createLineBorder(Color.LIGHT_GRAY, 1);
        mainPanel1.setBorder(BorderFactory.createTitledBorder(lineBorder1, Msg.prefDialogProfile.value())); //Profile settings
        //
        mainPanel1.add(new JLabel(Msg.prefDialogLabel.value()));//Configuration profile label:
        mainPanel1.add(createHelpButton(Msg.prefDialogLabelHelp)); //help for field
        label = new JTextField(profile.getLabel(), 12);
        label.setEditable(false);
        mainPanel1.add(label);
        mainPanel1.add(new JLabel(" "));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogDesc.value()));//Configuration profile description:
        mainPanel1.add(createHelpButton(Msg.prefDialogDescHelp)); //help for field
        description = new JTextField(profile.getDescription(), 12);
        mainPanel1.add(description);
        mainPanel1.add(new JLabel(" "));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogRepoloc.value()));//Repository locations:
        mainPanel1.add(createHelpButton(Msg.prefDialogRepolocHelp)); //help for field
        locations = new JList(profile.getRepositoryLocations().toArray());
        locations.setVisibleRowCount(4);
        JScrollPane locationsScrollPane = new JScrollPane(locations,
                                                  JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
                                                  JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
        locationsScrollPane.setPreferredSize(new Dimension(280, 72));
        mainPanel1.add(locationsScrollPane);
        LeaveGridLayout lgl = new LeaveGridLayout(1, 3);
        JPanel panel = new JPanel(lgl);
        mainPanel1.add(panel);
        panel.add(createImgButton(Icons.folderAdd, ActionCode.addRepsitoryLocation));
        panel.add(createImgButton(Icons.folderRemove, ActionCode.removeRepsitoryLocation));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogScanper.value()));//Scan period (sec):
        mainPanel1.add(createHelpButton(Msg.prefDialogScanperHelp)); //help for field
        scanPeriod = new JTextField(profile.getScanPeriod() != null
                                    ? profile.getScanPeriod().toString() : "", 12);
        mainPanel1.add(scanPeriod);
        periods = new JComboBox(Defaults.scanPeriods.keySet().toArray());
        mainPanel1.add(periods);
        periods.addActionListener(this);
        periods.setActionCommand(ActionCode.scanPeriodPresetSelected.name());
        //
        mainPanel1.add(new JLabel(Msg.prefDialogLaf.value()));//Look and Feel:
        mainPanel1.add(createHelpButton(Msg.prefDialogLafHelp)); //help for field
        Map<String,String> lafs = Defaults.lafs;
        laf = new JComboBox(lafs.values().toArray());
        mainPanel1.add(laf);
        laf.setSelectedItem(Defaults.lafs.get(profile.getLafClass()));
        laf.addActionListener(this);
        laf.setActionCommand(ActionCode.lafSelected.name());
        mainPanel1.add(new JLabel(" "));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogLaftheme.value()));//Look and Feel theme:
        mainPanel1.add(createHelpButton(Msg.prefDialogLafthemeHelp)); //help for field
        Map<String, Map<String,String>> lafThemes = Defaults.lafThemes;
        Map<String,String> currentLafThemes = lafThemes.get(profile.getLafClass());
        lafTheme = new JComboBox(currentLafThemes.keySet().toArray());
        mainPanel1.add(lafTheme);
        if(profile.getLafTheme() != null){
            lafTheme.setSelectedItem(profile.getLafTheme());
        }
        lafTheme.addActionListener(this);
        lafTheme.setActionCommand(ActionCode.lafThemeSelected.name());
        mainPanel1.add(new JLabel(" "));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogMedia.value())); //Select media for scan
        mainPanel1.add(createHelpButton(Msg.prefDialogMediaHelp)); //help for field
        JPanel smPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        mainPanel1.add(smPanel);
        smPanel.add(createImgButton(Icons.openSubdialog, ActionCode.openSelectMediaDialog));
        mainPanel1.add(new JLabel(" "));
        //
        mainPanel1.add(new JLabel(Msg.prefDialogAssignTools.value())); //Assign tools for file formats
        mainPanel1.add(createHelpButton(Msg.prefDialogAssignToolsHelp)); //help for field
        JPanel tffPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
        mainPanel1.add(tffPanel);
        tffPanel.add(createImgButton(Icons.openSubdialog, ActionCode.openFormatToolsDialog));
        mainPanel1.add(new JLabel(" "));

        // // //

        //Buttons
        JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        contentPane.add(buttonPanel, BorderLayout.PAGE_END);

        //
        buttonPanel.add(createButton(Msg.prefDialogSave, ActionCode.save));  //Save
        btnCancel = createButton(Msg.prefDialogCancel, ActionCode.cancel);
        buttonPanel.add(btnCancel);  //Cancel
    }

    private JButton createButton(Msg btnLabel, ActionCode actionCode){
        JButton button = new JButton(btnLabel.value());
        button.setActionCommand(actionCode.name());
        button.addActionListener(this);
        return button;
    }

    private JButton createImgButton(Icons icon, ActionCode actionCode){
        ImageIcon imageIcon = IconLoader.loadIcon(icon);
        JButton button = new JButton(imageIcon);
        Dimension size = new Dimension(imageIcon.getIconWidth() + 4,
                                       imageIcon.getIconHeight() + 4);
        button.setPreferredSize(size);
        button.setMaximumSize(size);
        button.setActionCommand(actionCode.name());
        button.addActionListener(this);
        return button;
    }

    private JButton createHelpButton(final Msg helpText){
        ImageIcon icon = IconLoader.loadIcon(Icons.help);
        JButton button = new JButton(icon);
        button.setPreferredSize(new Dimension(icon.getIconWidth()+4,
                                              icon.getIconHeight()+4));
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                TextDialog td = new TextDialog(PreferencesDialog.this, Msg.prefDialogHelp.value(), true);
                td.setBgColor(Color.WHITE);
                td.open("<body style=\"font-family: sans-serif;\">" + helpText.value() + "</body>");
            }
        });
        return button;
    }

    public void actionPerformed(ActionEvent event) {
        try {
            processAction(ActionCode.valueOf(event.getActionCommand()));
        }catch(IllegalInputException e){
            new WarningDialog().show(this, ExceptionUtils.translateException(e));
        }catch(Exception e){
            ExceptionDialog.show(this, e);
        }
    }

    @SuppressWarnings({"IfStatementWithTooManyBranches"})
    private void processAction(ActionCode actionCode){
        log.trace(LogMsg.inp("actionCode", actionCode));
        if(actionCode == ActionCode.cancel){
            actionCancel();
        }else if(actionCode == ActionCode.save){
            actionSave();
        }else if(actionCode == ActionCode.confPlacesSelected){
            actionConfPlaceSelected();
        }else if(actionCode == ActionCode.selectConfigPathDirectory){
            actionSelectConfigPathDirectory();
        }else if(actionCode == ActionCode.lafSelected){
            actionLafSelected();
        }else if(actionCode == ActionCode.lafThemeSelected){
            actionLafThemeSelected();
        }else if(actionCode == ActionCode.scanPeriodPresetSelected){
            actionScanPeriodPresetSelected();
        }else if(actionCode == ActionCode.addRepsitoryLocation){
            actionAddRepsitoryLocation();
        }else if(actionCode == ActionCode.removeRepsitoryLocation){
            actionRemoveRepsitoryLocation();
        }else if(actionCode == ActionCode.openSelectMediaDialog){
            actionOpenSelectMediaDialog();
        }else if(actionCode == ActionCode.openFormatToolsDialog){
            actionOpenFormatToolsDialog();
        }else{
            new InfoDialog().show(this, "Not yet implemented");
        }
    }

    private void actionCancel(){
        tmpProperties = null;
        profile = null;
        dispose();
    }

    private void actionSave(){
        //
        ListOption<Defaults.ConfigPlace, String> option
            = (ListOption<Defaults.ConfigPlace, String>) confPlaces.getSelectedItem();
        if(option != null){
            tmpProperties.put(Property.confPlace, option.getItemKey().name());
        }
        //Conf. place - path
        if(tmpProperties.get(Property.confPlace).equals(Defaults.ConfigPlace.path.name())){
            File f = new File(confPath.getText());
            if(!f.exists() || !f.isDirectory()){
                throw new IllegalInputException("Path is not valid for configuration directory (not exists or not direcotry)",
                                                Msg.prefDialogErrInvConfPath.getPropKey(),
                            ExceptionUtils.paramsToMap("path", f.getAbsolutePath()));
            }
        }
        tmpProperties.put(Property.confPath, confPath.getText());
        //
        ListOption<String,String> selectedLang = (ListOption<String, String>) language.getSelectedItem();
        if(selectedLang != null){
            tmpProperties.put(Property.uiLocale, selectedLang.getItemKey());
        }
        //
        profile.setLabel(label.getText());
        //
        profile.setDescription(description.getText());
        //
        profile.getRepositoryLocations().clear();
        ListModel listModel = locations.getModel();
        for (int i = 0; i < listModel.getSize(); i++) {
            profile.addRepositoryLocations(listModel.getElementAt(i).toString());
        }
        //Scan Period
        String _scanPeriod = scanPeriod.getText();
        Integer scanPeriodValue = null;
        if(!StringUtils.isNullOrEmpty(_scanPeriod)){
            if(!StringUtils.isInteger(_scanPeriod)){
                throw new IllegalInputException("Invalid value of field",
                                                Msg.prefDialogErrInvValue.getPropKey(),
                            ExceptionUtils.paramsToMap("field", Msg.prefDialogScanper.value()));
            }else{
                scanPeriodValue = new Integer(scanPeriod.getText());
            }
        }
        profile.setScanPeriod(scanPeriodValue);
        //L&F and Theme
        for (Map.Entry<String, String> lafEntry : Defaults.lafs.entrySet()) {
            if(lafEntry.getValue().equals(laf.getSelectedItem())){
                profile.setLafClass(lafEntry.getKey());
            }
        }
        //
        profile.setLafTheme((String) lafTheme.getSelectedItem());
        //Tools
        List<Tool> toolsInProfile = profile.getTools();
        List<Tool> toolsToRemove = new ArrayList<Tool>();
        for (Tool toolInProfile : toolsInProfile) {
            if(!tools.contains(toolInProfile)){
                toolsToRemove.add(toolInProfile);
            }
        }
        for (Tool tool : tools) {
            profile.addTool(tool);
        }
        // -unregister tool from associated file formats
        for (Tool toolToRemove : toolsToRemove) {
            profile.getTools().remove(toolToRemove);
            List<MediaType> mediaTypes = profile.getMediaTypes();
            for (MediaType mediaType : mediaTypes) {
                List<FileFormat> fileFormats = mediaType.getFileFormats();
                for (FileFormat fileFormat : fileFormats) {
                    fileFormat.getTools().remove(toolToRemove);
                }
            }
        }

        //
        dispose();
    }

    private void actionConfPlaceSelected(){
        ListOption<Defaults.ConfigPlace,String> selected
            = (ListOption<Defaults.ConfigPlace, String>) confPlaces.getSelectedItem();
        Defaults.ConfigPlace place = null;
        if(selected != null){
            place = selected.getItemKey();
        }
        if(confPathSelectDir != null){
            if(place == Defaults.ConfigPlace.path){
                confPathSelectDir.setEnabled(true);
            }else{
                confPathSelectDir.setEnabled(false);
            }
        }
    }

    private void actionSelectConfigPathDirectory(){
        String initDir = System.getProperty("user.home");
        File dir = FileChooser.selectDirectory(this, new File(initDir));
        if(dir != null){
            confPath.setText(dir.getAbsolutePath());
        }
    }

    private void actionLafSelected(){
        String lafClass = null;
        for (Map.Entry<String, String> entry : Defaults.lafs.entrySet()) {
            if(entry.getValue().equals(laf.getSelectedItem())){
                lafClass = entry.getKey();
                break;
            }
        }
        if(laf != null && lafTheme != null){
            Map<String, String> themes = Defaults.lafThemes.get(lafClass);
            lafTheme.setModel(new DefaultComboBoxModel(themes.keySet().toArray()));
        }
    }

    private void actionLafThemeSelected(){
    }

    private void actionScanPeriodPresetSelected(){
        String periodValue = Defaults.scanPeriods.get(periods.getSelectedItem().toString());
        scanPeriod.setText(periodValue);
    }

    private void actionAddRepsitoryLocation(){
        File dir = FileChooser.selectDirectory(this, new File(System.getProperty("user.home")));
        if(dir != null && dir.exists() && dir.isDirectory()){
            ListModel model = locations.getModel();
            DefaultListModel newModel = new DefaultListModel();
            for (int i = 0; i < model.getSize(); i++) {
                newModel.addElement(model.getElementAt(i));
            }

            String dirPath = dir.getAbsolutePath();
            if(!newModel.contains(dirPath)) {
                newModel.addElement(dirPath);
            }
            locations.setModel(newModel);
        }
    }

    private void actionRemoveRepsitoryLocation(){
        List selected = Arrays.asList(locations.getSelectedValues());
        ListModel model = locations.getModel();
        DefaultListModel newModel = new DefaultListModel();
        for (int i = 0; i < model.getSize(); i++) {
            Object item = model.getElementAt(i);
            if(!selected.contains(item)){
                newModel.addElement(item);
            }
        }
        locations.setModel(newModel);
    }

    private void actionOpenSelectMediaDialog(){
        SelectMediaDialog dialog = new SelectMediaDialog(this, profile.getMediaTypes(),
                                                         jitagDao);
        dialog.open();
        List<MediaType> newMediaTypes = dialog.getMediaTypes();
        if(newMediaTypes != null){
            profile.getMediaTypes().clear();
            for (MediaType newMediaType : newMediaTypes) {
                profile.addMediaType(newMediaType);
            }
        }
    }

    private void actionOpenFormatToolsDialog(){
        FormatToolsDialog formatToolsDialog = new FormatToolsDialog(this, profile);
        formatToolsDialog.open();
    }

}
